﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "GcmpModelInterface.h"
#include "IObject.h"

namespace gcmp
{
    class IDocument;
    class IElement;

    /// \brief 按需加载元素的过滤接口
    ///
    /// \see IDocument::LoadElements()
    class GCMP_MODEL_INTERFACE_EXPORT IElementLoadFilter : public IObject
    {
        DEFINE_CAST_DERIVED(gcmp::IElementLoadFilter, gcmp::IObject)
    public:
        /// \brief 创建Gcmp默认的加载过滤器。
        ///
        /// 产品可以重新实现这个接口，来完全控制所有对象的加载行为。或者复用平台的这个filter，改变、叠加产品自己认为需要处理的对象的自定义加载。
        /// 使用方式如下：
        /// class XXXElementLoadFilter : public IElementLoadFilter
        /// {
        /// public:
        ///     virtual bool OnLoadingElement(IDocument * pDocument, IElement * pElement, int32_t version) override
        ///     {
        ///         if (m_gcmpIElementLoadFilter ->OnLoadingElement())
        ///         { // 可以叠加、改变平台默认行为 }
        ///     }
        /// private:
        ///     OwnerPtr<IElementLoadFilter> m_gcmpIElementLoadFilter = IElementLoadFilter::CreateGcmpElementLoadFilter();
        /// };
        /// 行为是：
        ///     1. 如果正在加入的容器中，已经存在Id相同、相同版本对象，保留容器中已有对象、放弃正在加入的对象。并对Family、InstanceType、InstanceGRepHolder等特殊对象，定制了输出Log。
        ///     <行为补充到这里>
        /// \return 创建好的过滤器
        static OwnerPtr<IElementLoadFilter> CreateGcmpElementLoadFilter();

        /// \brief 对正在加载的元素，有机会进行行为定制。
        /// 
        /// \param pDocument 正在被加入的容器
        /// \param pElement 即将被加入的对象指针
        /// \param version 即将被加入的对象的版本
        /// \param identicalExistingElements 被收集的希望在后面批量报出的相同对象
        /// \return true 已经处理过，后面不需要加载；false，没在这里处理，需要后面走简易加载（将对象直接加入文档，无其它逻辑）
        virtual bool CustomizeLoadingElement(
            IDocument * pDocument, 
            IElement * pElement, 
            int32_t version,
            std::map<const IElement *, int32_t> & identicalExistingElements
        ) = 0;

        /// \brief 每次发现Id和版本都相同的对象时，回调
        ///
        /// \param pDocument 正在被加入的容器
        /// \param pExistingElement 已经存在对象的指针
        /// \param version 对象的版本
        virtual void OnIdenticalElementObserved(
            const IDocument * pDocument,
            const IElement * pExistingElement,
            int32_t version
        ) const = 0;

        /// \brief 发现Id和版本都相同的对象，批量回调
        ///
        /// \param pDocument 正在被加入的容器
        /// \param existingElements 已经存在对象的指针和版本
        virtual void OnIdenticalElementsObserved(
            const IDocument * pDocument,
            const std::map<const IElement *, int32_t> & existingElements
        ) const = 0;
    public:
        /// \brief 析构函数
        virtual ~IElementLoadFilter() = 0;
    };
}
